#include "stack_trace.h"

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
#include <cxxabi.h>
#include <iostream>
#include <cassert>

#include "stack_trace.h"

using namespace std;



string* demangle(char* symb){
    
    char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;
          // find parantheses and +address offset surrounding mangled name
        for (char *p = symb; *p; ++p)
        {
            if (*p == '(') 
            {
                mangled_name = p; 
            }
            else if (*p == '+') 
            {
                offset_begin = p;
            }
            else if (*p == ')')
            {
                offset_end = p;
                break;
            }
        }

        // if the line could be processed, attempt to demangle the symbol
        if (mangled_name && offset_begin && offset_end && 
            mangled_name < offset_begin)
        {
            *mangled_name++ = '\0';
            *offset_begin++ = '\0';
            *offset_end++ = '\0';
            
            int status;
            char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
            string * ligne = new string();
            
            
            //real_name contient le nom de la fonction
            //symb contient le nom de l'executable
            
            if(status==0){
                /*on copie dans dest real_name*/
                /*Construire ici la nouvelle chaine de cara ici puis la copier dans symb*/
                ligne->append(symb);
                ligne->append("#");
                ligne->append(real_name);
                free(real_name);   
            }
            else{
                ligne->append(symb);
                ligne->append("#");
                ligne->append(mangled_name);  
            }
            
            return ligne;
        }
        // otherwise, print the whole line
        else
        {
            //std::cerr << "[bt]: (" << "i" << ") " << sym->exec << std::endl;
            return NULL;
        }
}
static int compteur=0;
callers_tree* demangle_(symbole_t * sym)
{
    
    //il faut d'abord recuperer les deux parties du symbole   'part1#part2'    

    compteur++;
    
    char* caller_1=NULL;
    char* caller_2=NULL;
    
    
    
    caller_1=strtok(sym->symb,DELIMITEUR);
    caller_2=strtok(NULL,DELIMITEUR);
    
    string* s1=(caller_1==NULL)?NULL:demangle(caller_1);
    string* s2=(caller_2==NULL)?NULL:demangle(caller_2);
    
    callers_tree* callers_ = new callers_tree;
    callers_->parent=s1;
    callers_->p_parent=s2;
    //printf("BT[%d]:%s|%s|\n",compteur,(callers_->p_parent==NULL)   ?NULL:callers_->p_parent->c_str(),
    //                       (callers_->parent==NULL)     ?NULL:callers_->parent->c_str());
    
    
    return callers_;
}



#include <cassert>


/**
 * 
 * @param content
 * @param level
 */
void getCallerByLevel(symbole_t* content,int level){
    void * array[level];
    int size = backtrace(array, level);
    //assert(size>=level);
    /*contient les stack frames*/
    char ** messages = backtrace_symbols(array, level);    
    
    memset(content->symb,0,MAX_SYMBOLE_LENGTH);
    //on va concatener messages[level-1] et messages[level-2] dans content
    strncat(content->symb,messages[size-1],strlen(messages[size-1]));
    strncat(content->symb,DELIMITEUR,1);
    
    strncat(content->symb,messages[size-3],strlen(messages[size-3]));
    
    for(int i=0;i<level;i++){
        //printf("MERDE[%d]: %s\n",i,messages[i]);
        //sleep(1);
    }
    free(messages);
}


/*** 
 * @param content
 */
void getCaller(symbole_t* content){
    
    
    getCallerByLevel(content, 6);
    
    //printf("<%s>\n",content->symb);
}
/**
 * 
 * @param content
 * @param level
 * @param fd
 */
void getCallerByLevel_fd(int level, int fd){
    void * array[level];
    int size = backtrace(array, level);
    assert(size>=level);
    backtrace_symbols_fd(array, level,fd);
}

/**
 * 
 * @param content
 * @param fd
 */
void getCaller_fd(int fd){
    getCallerByLevel_fd(5,fd);
    
}
